package org.example.me.queue;

import lombok.extern.slf4j.Slf4j;
import org.example.me.entity.ExcelLog;
import org.example.me.service.ExcelExportService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Excel导出队列处理器
 * 限制消费个数，使用阻塞队列消费导出任务，避免重复消费
 */
@Slf4j
@Component
public class ExcelExportQueue {

    @Autowired
    private ExcelExportService excelExportService;

    /**
     * 阻塞队列，用于存放待处理的导出任务
     */
    private final BlockingQueue<ExportTask> taskQueue = new LinkedBlockingQueue<>();

    /**
     * 正在处理的任务集合，用于避免重复消费
     */
    private final ConcurrentHashMap<String, Object> processingTasks = new ConcurrentHashMap<>();

    /**
     * 任务处理线程运行状态标识
     */
    private final AtomicBoolean running = new AtomicBoolean(true);

    /**
     * 单线程线程池，用于处理导出任务
     */
    private ExecutorService executorService;

    /**
     * 初始化任务处理线程
     */
    @PostConstruct
    public void init() {
        // 创建单线程线程池来处理任务
        executorService = Executors.newSingleThreadExecutor();
        
        executorService.submit(this::processTasks);
        log.info("Excel导出任务处理器已启动");
    }

    /**
     * 销毁Bean时关闭任务处理器
     */
    @PreDestroy
    public void destroy() {
        shutdown();
    }

    /**
     * 关闭任务处理器
     */
    public void shutdown() {
        running.set(false);
        if (executorService != null) {
            executorService.shutdown();
        }
        log.info("Excel导出任务处理器已关闭");
    }

    /**
     * 添加导出任务到队列
     *
     * @param excelLog Excel日志信息
     */
    public void addExportTask(ExcelLog excelLog) {
        ExportTask task = new ExportTask(excelLog);
        String taskId = task.getTaskId();
        
        // 检查是否正在处理或已在队列中
        if (!processingTasks.containsKey(taskId) && !isTaskInQueue(taskId)) {
            try {
                taskQueue.put(task);
                log.info("添加导出任务到队列，任务ID: {}", taskId);
            } catch (InterruptedException e) {
                log.error("添加导出任务失败，任务ID: {}", taskId, e);
                Thread.currentThread().interrupt();
            }
        } else {
            log.info("导出任务已在处理队列中，跳过添加，任务ID: {}", taskId);
        }
    }

    /**
     * 检查任务是否已在队列中
     *
     * @param taskId 任务ID
     * @return true-队列中已存在该任务，false-队列中不存在该任务
     */
    private boolean isTaskInQueue(String taskId) {
        return taskQueue.stream().anyMatch(task -> task.getTaskId().equals(taskId));
    }

    /**
     * 持续处理队列中的导出任务
     */
    private void processTasks() {
        while (running.get()) {
            try {
                // 从阻塞队列中取出任务
                ExportTask task = taskQueue.take();

                // 检查任务是否已经在处理中
                if (processingTasks.putIfAbsent(task.getTaskId(), new Object()) != null) {
                    log.info("导出任务已在处理中，跳过执行，任务ID: {}", task.getTaskId());
                    continue;
                }

                // 直接在当前线程中处理任务
                processTask(task);
            } catch (InterruptedException e) {
                log.warn("任务处理线程被中断");
                Thread.currentThread().interrupt();
                break;
            } catch (Exception e) {
                log.error("处理导出任务时发生异常", e);
            }
        }
    }

    /**
     * 直接在当前线程中处理单个导出任务
     *
     * @param task 导出任务
     */
    private void processTask(ExportTask task) {
        String taskId = task.getTaskId();
        try {
            log.info("开始处理导出任务，任务ID: {}", taskId);
            excelExportService.export(task.getExcelLog());
            log.info("导出任务处理完成，任务ID: {}", taskId);
        } catch (Exception e) {
            log.error("导出任务处理失败，任务ID: {}", taskId, e);
        } finally {
            // 任务处理完成后从正在处理集合中移除
            processingTasks.remove(taskId);
        }
    }

}